using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using CodePlex.TfsLibrary.ClientEngine;
using CodePlex.TfsLibrary.ObjectModel;

namespace CodePlex.CodePlexClientLibrary
{
    public class CommitConsoleView : ICommitView
    {
        const string ignoreLine = "=== Everything below this line will be ignored ===";
        readonly List<SourceItem> commitList = new List<SourceItem>();
        readonly string editor;
        readonly ITfsEngine engine;
        readonly string localPath;
        string message;
        string tfsServerPath;
        string tfsServerUrl;

        public CommitConsoleView(ITfsEngine engine,
                                 string editor,
                                 string message,
                                 string localPath)
        {
            this.engine = engine;
            this.editor = editor;
            this.message = message;
            this.localPath = localPath;
        }

        protected ITfsEngine Engine
        {
            get { return engine; }
        }

        protected string LocalPath
        {
            get { return localPath; }
        }

        public string Message
        {
            get { return message; }
            protected set { message = value; }
        }

        public string TfsServerPath
        {
            protected get { return tfsServerPath; }
            set { tfsServerPath = value; }
        }

        public string TfsServerUrl
        {
            protected get { return tfsServerUrl; }
            set { tfsServerUrl = value; }
        }

        public void DisplayError(string path,
                                 SourceItemResult result)
        {
            ConsoleUtil.PrintCommandError(path, result);
        }

        public void DisplayItem(SourceItem item)
        {
            switch (item.LocalItemStatus)
            {
                case SourceItemStatus.Add:
                case SourceItemStatus.Conflict:
                case SourceItemStatus.Delete:
                case SourceItemStatus.Modified:
                    commitList.Add(item);
                    break;
            }
        }

        public void DisplayNoItemsMessage()
        {
            Console.WriteLine("Nothing to commit.");
        }

        void LaunchExternalEditor(IList<SourceItem> localChanges)
        {
            string messageFile = WriteMessageFileForEditor(localChanges);
            CodePlexClientUtil.StartProcess(editor, messageFile).WaitForExit();
            message = ReadMessageFile(messageFile);
        }

        public bool QueryForResolve(string filename)
        {
            throw new NotImplementedException();
        }

        static string ReadMessageFile(string messageFile)
        {
            string text = File.ReadAllText(messageFile);
            File.Delete(messageFile);

            int idx = text.IndexOf(ignoreLine);
            if (idx >= 0)
                text = text.Substring(0, idx);

            return text.Trim();
        }

        public void RemoveItem(string path) {}

        public void Run()
        {
            new CommitViewPresenter(this, Engine).Run(LocalPath);
        }

        public void ShowComplete(int changesetId)
        {
            Console.WriteLine("Committed change set {0}.", changesetId);
        }

        public void ShowItemStatus(SourceItem item,
                                   SourceItemResult result)
        {
            if (result != SourceItemResult.S_Ok)
                ConsoleUtil.PrintCommandError(item.LocalName, result);
            else
            {
                string operation = "Uploading";

                if (item.LocalItemStatus == SourceItemStatus.Delete)
                    operation = "Deleting";

                Console.WriteLine("{0}: {1}", operation, CodePlexClientUtil.GetRelativePath(Directory.GetCurrentDirectory(), item.LocalName));
            }
        }

        public virtual bool ShowView()
        {
            if (commitList.Count == 0)
            {
                Console.WriteLine("Nothing to commit.");
                return false;
            }

            if (Message == null)
            {
                LaunchExternalEditor(commitList);

                if (string.IsNullOrEmpty(Message))
                {
                    Console.WriteLine("Won't commit with an empty message.");
                    return false;
                }

                Console.Write("Proceed with commit? [Y]es, [N]o ? (Y) ");
                string answer = Console.ReadLine().ToLowerInvariant();
                return (answer == "yes" || answer == "y" || answer == "");
            }

            return true;
        }

        string WriteMessageFileForEditor(IEnumerable<SourceItem> localChanges)
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendLine();
            builder.AppendLine(ignoreLine);
            builder.AppendLine();

            foreach (SourceItem item in localChanges)
                builder.AppendFormat("{0}    {1}{2}", item.LocalItemStatus.ToString().Substring(0, 1),
                                     CodePlexClientUtil.GetRelativePath(LocalPath, item.LocalName), Environment.NewLine);

            string result = Path.GetTempFileName();
            File.WriteAllText(result, builder.ToString());
            return result;
        }
    }
}